/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace DaveSexton.DocProject
{
	public class MarshalByRefCollection<T> : MarshalByRefObject, IList<T>, IList
	{
		#region Private / Protected
		protected IList<T> Items
		{
			get
			{
				return list;
			}
		}

		private object sync;
		private readonly IList<T> list;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="MarshalByRefCollection" /> class.
		/// </summary>
		public MarshalByRefCollection()
		{
			list = new List<T>();
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="MarshalByRefCollection" /> class.
		/// </summary>
		public MarshalByRefCollection(IList<T> items)
		{
			if (items == null)
				throw new ArgumentNullException("items");

			this.list = items;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="MarshalByRefCollection" /> class.
		/// </summary>
		public MarshalByRefCollection(IList<T> items, bool readOnly)
		{
			if (items == null)
				throw new ArgumentNullException("items");

			if (readOnly && !items.IsReadOnly)
				this.list = new System.Collections.ObjectModel.ReadOnlyCollection<T>(items);
			else
				this.list = items;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="MarshalByRefCollection" /> class.
		/// </summary>
		public MarshalByRefCollection(IEnumerable<T> items)
		{
			if (items == null)
				throw new ArgumentNullException("items");

			this.list = new List<T>(items);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="MarshalByRefCollection" /> class.
		/// </summary>
		public MarshalByRefCollection(IEnumerable<T> items, bool readOnly)
		{
			if (items == null)
				throw new ArgumentNullException("items");

			if (readOnly)
				this.list = new List<T>(items).AsReadOnly();
			else
				this.list = new List<T>(items);
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="MarshalByRefCollection" /> class.
		/// </summary>
		public MarshalByRefCollection(EnumerableBuilder<T> items)
		{
			if (items == null)
				throw new ArgumentNullException("items");

			this.list = new List<T>(items());
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="MarshalByRefCollection" /> class.
		/// </summary>
		public MarshalByRefCollection(EnumerableBuilder<T> items, bool readOnly)
		{
			if (items == null)
				throw new ArgumentNullException("items");

			if (readOnly)
				this.list = new List<T>(items()).AsReadOnly();
			else
				this.list = new List<T>(items());
		}
		#endregion

		#region Methods
		[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand,
			Flags = System.Security.Permissions.SecurityPermissionFlag.Infrastructure)]
		public override object InitializeLifetimeService()
		{
			return null;
		}

		protected virtual void InsertItem(int index, T item)
		{
			list.Insert(index, item);
		}

		protected virtual void SetItem(int index, T item)
		{
			list[index] = item;
		}

		protected virtual void ClearItems()
		{
			list.Clear();
		}

		protected virtual void RemoveItem(int index)
		{
			list.RemoveAt(index);
		}
		#endregion

		#region IList<T> Members
		public T this[int index]
		{
			get
			{
				return list[index];
			}
			set
			{
				if (list.IsReadOnly)
					throw new NotSupportedException(Resources.Errors.ReadOnlyCollection);

				if (index < 0 || index >= list.Count)
					throw new ArgumentOutOfRangeException("index");

				SetItem(index, value);
			}
		}

		public int IndexOf(T item)
		{
			return list.IndexOf(item);
		}

		public void Insert(int index, T item)
		{
			if (list.IsReadOnly)
				throw new NotSupportedException(Resources.Errors.ReadOnlyCollection);

			if (index < 0 || index > list.Count)
				throw new ArgumentOutOfRangeException("index");

			InsertItem(index, item);
		}

		public void RemoveAt(int index)
		{
			if (list.IsReadOnly)
				throw new NotSupportedException(Resources.Errors.ReadOnlyCollection);

			if (index < 0 || index >= list.Count)
				throw new ArgumentOutOfRangeException("index");

			RemoveItem(index);
		}
		#endregion

		#region ICollection<T> Members
		public void Add(T item)
		{
			if (list.IsReadOnly)
				throw new NotSupportedException(Resources.Errors.ReadOnlyCollection);

			InsertItem(list.Count, item);
		}

		public void Clear()
		{
			if (list.IsReadOnly)
				throw new NotSupportedException(Resources.Errors.ReadOnlyCollection);

			ClearItems();
		}

		public bool Contains(T item)
		{
			return list.Contains(item);
		}

		public void CopyTo(T[] array, int arrayIndex)
		{
			list.CopyTo(array, arrayIndex);
		}

		public int Count
		{
			get
			{
				return list.Count;
			}
		}

		bool ICollection<T>.IsReadOnly
		{
			get
			{
				return list.IsReadOnly;
			}
		}

		public bool Remove(T item)
		{
			if (list.IsReadOnly)
				throw new NotSupportedException(Resources.Errors.ReadOnlyCollection);

			int index = list.IndexOf(item);

			if (index < 0)
				return false;

			RemoveItem(index);
			return true;
		}
		#endregion

		#region IEnumerable<T> Members
		[System.Diagnostics.DebuggerHidden]
		public IEnumerator<T> GetEnumerator()
		{
			return new MarshalByRefEnumerator<T>(list);
		}
		#endregion

		#region IEnumerable Members
		[System.Diagnostics.DebuggerHidden]
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
		#endregion

		#region IList Members
		int IList.Add(object value)
		{
			if (list.IsReadOnly)
				throw new NotSupportedException(Resources.Errors.ReadOnlyCollection);

			Add((T) value);
			return Count - 1;
		}

		bool IList.Contains(object value)
		{
			return Contains((T) value);
		}

		int IList.IndexOf(object value)
		{
			return IndexOf((T) value);
		}

		void IList.Insert(int index, object value)
		{
			if (list.IsReadOnly)
				throw new NotSupportedException(Resources.Errors.ReadOnlyCollection);

			Insert(index, (T) value);
		}

		protected bool IsFixedSize { get { return ((IList) this).IsFixedSize; } }

		bool IList.IsFixedSize
		{
			get
			{
				IList items = list as IList;

				return items != null && items.IsFixedSize;
			}
		}

		protected bool IsReadOnly { get { return list.IsReadOnly; } }

		bool IList.IsReadOnly
		{
			get
			{
				return list.IsReadOnly;
			}
		}

		void IList.Remove(object value)
		{
			if (list.IsReadOnly)
				throw new NotSupportedException(Resources.Errors.ReadOnlyCollection);

			Remove((T) value);
		}

		object IList.this[int index]
		{
			get
			{
				return list[index];
			}
			set
			{
				this[index] = (T) value;
			}
		}
		#endregion

		#region ICollection Members
		protected bool IsSynchronized { get { return false; } }

		bool ICollection.IsSynchronized
		{
			get
			{
				return false;
			}
		}

		protected object SyncRoot { get { return ((IList) this).SyncRoot; } }

		object ICollection.SyncRoot
		{
			get
			{
				if (sync == null)
				{
					ICollection items = list as ICollection;

					if (items != null)
						sync = items.SyncRoot;
					else
						System.Threading.Interlocked.CompareExchange(ref sync, new object(), null);
				}

				return sync;
			}
		}

		void ICollection.CopyTo(Array array, int index)
		{
			if (array == null)
				throw new ArgumentNullException("array");

			if (array.Rank != 1 || array.GetLowerBound(0) != 0)
				throw new ArgumentOutOfRangeException("array");

			if (index < 0 || array.Length - index < Count)
				throw new ArgumentOutOfRangeException("index");

			T[] local = array as T[];

			if (local != null)
				list.CopyTo(local, index);
			else
			{
				Type type = array.GetType().GetElementType();
				Type genericType = typeof(T);

				if (!type.IsAssignableFrom(genericType) && !genericType.IsAssignableFrom(type))
					throw new ArrayTypeMismatchException();

				object[] objects = array as object[];

				if (objects == null)
					throw new ArrayTypeMismatchException();

				int count = list.Count;

				for (int i = 0; i < count; i++)
					objects[index++] = list[i];
			}
		}
		#endregion
	}
}
